package com.kkwrite.regimen.provider.service.order;

import com.kkwrite.regimen.common.constant.Constant;
import com.kkwrite.regimen.common.constant.DeletedEnum;
import com.kkwrite.regimen.common.constant.OrderStatusEnum;
import com.kkwrite.regimen.common.constant.RegPictureEnum;
import com.kkwrite.regimen.common.constant.OrderStatusEnum.OrderType;
import com.kkwrite.regimen.common.entity.dto.OutDTO;
import com.kkwrite.regimen.common.entity.dto.order.OrderChangePayStatusInDTO;
import com.kkwrite.regimen.common.entity.dto.order.OrderCountOutDTO;
import com.kkwrite.regimen.common.entity.dto.order.OrderDetailDTO;
import com.kkwrite.regimen.common.entity.dto.order.OrderPageableOutDTO;
import com.kkwrite.regimen.common.entity.dto.order.OrderReviewSubmitInDTO;
import com.kkwrite.regimen.common.entity.dto.order.RefundOutDTO;
import com.kkwrite.regimen.common.entity.dto.order.SubmitOrderInDTO;
import com.kkwrite.regimen.provider.domain.RegActivityApplyDO;
import com.kkwrite.regimen.provider.domain.RegOrderDO;
import com.kkwrite.regimen.provider.exception.DBNullException;
import com.kkwrite.regimen.provider.exception.OrderCannotCancelException;
import com.kkwrite.regimen.provider.exception.OrderRefundException;
import com.kkwrite.regimen.provider.exception.RegimenProviderException;
import com.kkwrite.regimen.provider.exception.ServiceException;
import com.kkwrite.regimen.provider.repository.RegActivityApplyRepository;
import com.kkwrite.regimen.provider.repository.RegActivityRepository;
import com.kkwrite.regimen.provider.repository.RegOrderRepository;
import com.kkwrite.regimen.provider.service.item.ItemService;
import com.kkwrite.regimen.provider.service.review.ReviewService;
import com.kkwrite.regimen.provider.service.shop.ShopService;
import com.kkwrite.regimen.provider.stream.dto.IncomeRecordDTO;
import com.kkwrite.regimen.provider.stream.dto.IncreaseReviewCountDTO;
import com.kkwrite.regimen.provider.stream.dto.IncreaseSalesCountDTO;
import com.kkwrite.regimen.provider.stream.dto.RewardPointDTO;
import com.kkwrite.regimen.provider.stream.service.IncomeRecordService;
import com.kkwrite.regimen.provider.stream.service.IncreaseReviewCountService;
import com.kkwrite.regimen.provider.stream.service.IncreaseSalesCountService;
import com.kkwrite.regimen.provider.stream.service.RewardPointService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import javax.validation.constraints.NotNull;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import static com.kkwrite.regimen.common.constant.Constant.BOOLEAN_STRING_TRUE;

/**
 * 订单服务
 *
 * @author Soosky Wang
 * @version 1.0.0
 */
@Service("orderService")
public class OrderServiceImpl implements OrderService {

    private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);

    @Resource
    private IncreaseSalesCountService increaseSalesCountService;
    @Resource
    private IncreaseReviewCountService increaseReviewCountService;
    @Resource
    private IncomeRecordService incomeRecordService;

    @Resource(name = "regOrderRepository")
    private RegOrderRepository regOrderRepository;

    @Resource(name = "shopService")
    private ShopService shopService;
    @Resource(name = "reviewService")
    private ReviewService reviewService;
    @Resource(name = "itemService")
    private ItemService itemService;
    @Resource
    private RewardPointService rewardPointService;
    
    @Resource(name = "regActivityApplyRepository")
	private RegActivityApplyRepository regActivityApplyRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OutDTO<String> save(SubmitOrderInDTO inDTO, Integer userId) {
        RegOrderDO order = new RegOrderDO();
        order.setOrderCode(generateUniqueCode());
        order.setOrderType(inDTO.getOrderType());
        order.setItemId(Long.valueOf(inDTO.getItemId()));
        order.setShopId(Long.valueOf(inDTO.getShopId()));
        order.setUserId(Long.valueOf(userId));
        order.setPhoneNum(inDTO.getPhoneNum());
        order.setOrderStatus(OrderStatusEnum.UNPAID.getStatusValue());
        order.setOrderAmount(inDTO.getNum() * inDTO.getPrice());
        order.setOrderQuantity(inDTO.getNum());
        order.setPayStatus(OrderStatusEnum.PayStatusEnum.UNPAID.getPayStatus());
        order.setRefund(OrderStatusEnum.IsRefund.IS_REFUND_NO.getStatus());
        Date date = new Date();
        order.setValidTime(date);
        order.setInvalidTime(calculationInvalidTime(date));
        order.setReviewed(OrderStatusEnum.IsReviewed.NO.getStatus());
        order.setShopReviewed(OrderStatusEnum.IsReviewed.NO.getStatus());
        order.setDeleted(DeletedEnum.UN_DELETED.getValue());
        regOrderRepository.save(order);
        if (order.getOrderId() == null) {
            return new OutDTO<>("false", "订单保存异常！");
        }
        IncreaseSalesCountDTO countDTO = new IncreaseSalesCountDTO();
        countDTO.setItemId(inDTO.getItemId());
        countDTO.setCount(inDTO.getNum());
        if(!order.getOrderType().equals(OrderType.ACTIVITY_ORDER_TYPE.getOrderType())){
        	increaseSalesCountService.send(countDTO);
        }
        return new OutDTO<>("true", null, null, order.getOrderCode());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OutDTO updatePayStatus(Map<String, String> map) {
        RegOrderDO queriedOrder = regOrderRepository.findByOrderCode(map.get("out_trade_no"));
        if (queriedOrder.getPayStatus().equals(OrderStatusEnum.PayStatusEnum.PAID.getPayStatus())) {
            return new OutDTO("true", null);
        }
        int needFee = (int) (queriedOrder.getOrderAmount() * 100);
        if (needFee != Integer.valueOf(map.get("total_fee"))) {
            return new OutDTO("false", "订单支付状态更新失败");
        }
        try {
            updatePayStatus(map, queriedOrder);
        } catch (Exception e) {
            return new OutDTO("false", "订单支付状态更新失败");
        }
        return new OutDTO("true", "更新订单支付状态完成");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(String orderCode) throws DBNullException, OrderCannotCancelException {
        Optional<RegOrderDO> orderOptional = Optional.ofNullable(regOrderRepository.findByOrderCode(orderCode));
        RegOrderDO queriedOrder = orderOptional.orElseThrow(DBNullException::new);
        if (queriedOrder.getOrderStatus().equals(OrderStatusEnum.CANCELED.getStatusValue())) {
            return;
        }
        if (queriedOrder.getServiceStartTime() != null) {
            throw new OrderCannotCancelException("服务已开始不能取消");
        }
        if (queriedOrder.getPayStatus().equals(OrderStatusEnum.PayStatusEnum.PAID.getPayStatus())) {
            queriedOrder.setRefund(OrderStatusEnum.IsRefund.IS_REFUND_YES.getStatus());
            queriedOrder.setRefundSate(OrderStatusEnum.RefundStatus.PENDING_REFUND.getStatus());
        }
        queriedOrder.setOrderStatus(OrderStatusEnum.CANCELED.getStatusValue());
        regOrderRepository.saveAndFlush(queriedOrder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeServiceStatus(String orderCode, Integer type) throws DBNullException {
        Optional<RegOrderDO> orderOptional = Optional.ofNullable(regOrderRepository.findByOrderCode(orderCode));
        RegOrderDO queriedOrder = orderOptional.orElseThrow(DBNullException::new);
        switch (type) {
            case 0:
                if (queriedOrder.getServiceEndTime() != null) {
                    return;
                }
                if (queriedOrder.getServiceStartTime() == null) {
                    throw new ServiceException("服务未开始不能结束！");
                }
                queriedOrder.setServiceEndTime(new Timestamp(System.currentTimeMillis()));
                queriedOrder.setOrderStatus(OrderStatusEnum.COMPLETED.getStatusValue());
                break;
            case 1:
                if (queriedOrder.getServiceStartTime() != null) {
                    return;
                }
                if (!queriedOrder.getOrderStatus().equals(OrderStatusEnum.UNCONSUME.getStatusValue())) {
                    throw new ServiceException("只有待消费的订单才能开始服务");
                }
                if (queriedOrder.getPayStatus().equals(OrderStatusEnum.PayStatusEnum.UNPAID.getPayStatus())) {
                    throw new ServiceException("订单未支付");
                }
                queriedOrder.setServiceStartTime(new Timestamp(System.currentTimeMillis()));
                break;
            default:
        }
        regOrderRepository.save(queriedOrder);
        if (type == 0) {
            int userId = queriedOrder.getUserId().intValue();
            // 服务结束，奖励积分
            int amount = (int) (double) queriedOrder.getOrderAmount();
            if (!rewardPointService.send(new RewardPointDTO(userId, amount, "消费订单完成"))) {
                throw new ServiceException("消息发送失败");
            }
            // 邀请收益
            IncomeRecordDTO record = new IncomeRecordDTO();
            record.setUserId(userId);
            record.setOrderCode(orderCode);
            record.setAmount(queriedOrder.getOrderAmount());
            record.setShopId(queriedOrder.getShopId().intValue());
            incomeRecordService.send(record);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OrderPageableOutDTO queryOrderPageableExceptUnpay(String shopIdString, Integer page, Integer pageSize) {
        final long shopId = Long.parseLong(shopIdString);
        Page<RegOrderDO> pageData = regOrderRepository.findAll((root, query, cb) -> {
            Predicate p1 = cb.equal(root.get("shopId"), shopId);
            Predicate p2 = cb.notEqual(root.get("orderStatus"), OrderStatusEnum.UNPAID.getStatusValue());
            return query.where(cb.and(p1, p2)).getRestriction();
        }, PageRequest.of(page, pageSize, new Sort(Sort.Direction.DESC, "gmtCreate")));
        if (pageData.getTotalElements() == 0) {
            throw new ServiceException("未查询到订单");
        }
        return processPageableOrderData(pageData);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OrderPageableOutDTO queryOrderPageable(Integer page, Integer pageSize, Integer userId,
                                                  Integer shopId, Short status) throws DBNullException {
        return queryOrderPageable(page, pageSize, userId, shopId, status, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OrderPageableOutDTO queryOrderPageable(Integer page, Integer pageSize, Integer userId,
                                                   Integer shopId, Short status, Short orderType) throws DBNullException {
        RegOrderDO example = new RegOrderDO();
        if (shopId != null) {
            example.setShopId(shopId.longValue());
        }
        if (userId != null) {
            example.setUserId(userId.longValue());
        }
        if (orderType != null) {
            example.setOrderType(orderType);
        }
        Page<RegOrderDO> pageData = regOrderRepository.findAll(Example.of(example), PageRequest.of(page, pageSize, new Sort(Sort.Direction.DESC, "gmtCreate")));
        if (pageData.getTotalElements() == 0) {
            throw new DBNullException();
        }
        return processPageableOrderData(pageData);
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OrderPageableOutDTO processPageableOrderData(Page<RegOrderDO> pageData) {
        OrderPageableOutDTO out = new OrderPageableOutDTO();
        out.setTotalPage(pageData.getTotalPages());
        out.setTotalElements(pageData.getTotalElements());
        List<OrderPageableOutDTO.OrderData> outList = out.getData();
        pageData.getContent().forEach((orderDO) -> {
            OrderPageableOutDTO.OrderData data = out.createOrderData();
            BeanUtils.copyProperties(orderDO, data);
            data.setRefundStatus(orderDO.getRefundSate());
            data.setCreateTime(orderDO.getGmtCreate().toLocalDateTime());
            try {
                Optional.ofNullable(itemService.getItemDetail(orderDO.getItemId())).ifPresent(item -> {
                    data.setItemName(item.getItemName());
                    data.setDuration(item.getDuration());
                    item.getPictures().forEach((picture) -> {
                        if (picture.getPictureType().equals(RegPictureEnum.PictureTypeEnum.INTRODUCTION_PICTURE.getType())) {
                            data.setPicturePath(picture.getPictureUrl());
                        }
                    });
                });
            } catch (ServiceException e) {
                throw new RegimenProviderException("订单项目详情查询异常");
            }
            outList.add(data);
        });
        return out;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RefundOutDTO refund(String orderCode) throws DBNullException, OrderRefundException {
        Optional<RegOrderDO> orderOptional = Optional.ofNullable(regOrderRepository.findByOrderCode(orderCode));
        RegOrderDO queriedOrder = orderOptional.orElseThrow(DBNullException::new);
        if (queriedOrder.getRefund().equals(OrderStatusEnum.IsRefund.IS_REFUND_NO.getStatus())) {
            throw new OrderRefundException("该订单不需要退款");
        }
        if (queriedOrder.getRefundCode() != null) {
            RefundOutDTO out = new RefundOutDTO();
            out.setTransactionId(queriedOrder.getTransactionId());
            out.setAmount(String.valueOf((int) (queriedOrder.getOrderAmount() * 100)));
            out.setRefundCode(queriedOrder.getRefundCode());
            return out;
        }
        if (queriedOrder.getGmtCreate().toLocalDateTime().plusYears(1L).isBefore(LocalDateTime.now())) {
            throw new OrderRefundException("超过一年的订单不能退款");
        }
        String refundCode = generateUniqueCode();
        queriedOrder.setRefundCode(refundCode);
        regOrderRepository.saveAndFlush(queriedOrder);
        RefundOutDTO out = new RefundOutDTO();
        out.setTransactionId(queriedOrder.getTransactionId());
        out.setAmount(String.valueOf((int) (queriedOrder.getOrderAmount() * 100)));
        out.setRefundCode(refundCode);
        return out;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> continuePay(String orderCode) throws DBNullException {
        Optional<RegOrderDO> orderOptional = Optional.ofNullable(regOrderRepository.findByOrderCode(orderCode));
        RegOrderDO queriedOrder = orderOptional.orElseThrow(DBNullException::new);
        if (!queriedOrder.getOrderStatus().equals(OrderStatusEnum.UNPAID.getStatusValue())) {
            throw new ServiceException("该订单不能继续支付");
        }
        String newCode = generateUniqueCode();
        queriedOrder.setOrderCode(newCode);
        regOrderRepository.saveAndFlush(queriedOrder);
        Map<String, String> map = new HashMap<>(16);
        map.put("orderCode", newCode);
        map.put("totalFee", String.valueOf((int) (queriedOrder.getOrderAmount() * 100)));
        if(queriedOrder.getOrderType().equals(OrderType.ACTIVITY_ORDER_TYPE.getOrderType())){
        	RegActivityApplyDO regActivityApplyDO  = regActivityApplyRepository.findOneByOrderCode(orderCode);
        	regActivityApplyDO.setOrderCode(newCode);
        	regActivityApplyRepository.saveAndFlush(regActivityApplyDO);
        }
        return map;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTradeState(final Map<String, String> paramMap) {
        if (!paramMap.containsKey("trade_state") || !paramMap.containsKey("out_trade_no")) {
            return;
        }
        Optional<RegOrderDO> optional = Optional.ofNullable(regOrderRepository.findByOrderCode(paramMap.get("out_trade_no")));
        optional.ifPresent(order -> {
            switch (OrderStatusEnum.WechatTradeState.valueOf(paramMap.get("trade_state"))) {
                case SUCCESS:
                    if (order.getPayStatus().equals(OrderStatusEnum.PayStatusEnum.PAID.getPayStatus())) {
                        return;
                    }
                    try {
                        updatePayStatus(paramMap, order);
                    } catch (Exception e) {
                        logger.error(e.getMessage(), e);
                    }
                    break;
                case REFUND:
                    break;
                default:
            }
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRefundInfo(Map<String, String> paramMap) throws DBNullException {
        Optional<RegOrderDO> optional = Optional.ofNullable(regOrderRepository.findByOrderCode(paramMap.get("out_trade_no")));
        RegOrderDO queriedOrder = optional.orElseThrow(DBNullException::new);
        if (queriedOrder.getRefundSate().equals(OrderStatusEnum.RefundStatus.REFUNDED.getStatus())) {
            return;
        }
        queriedOrder.setRefundSate(OrderStatusEnum.RefundStatus.REFUNDED.getStatus());
        queriedOrder.setRefundId(paramMap.get("refund_id"));
        queriedOrder.setRefundSuccessTime(
                Timestamp.valueOf(LocalDateTime.parse(paramMap.get("success_time"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        regOrderRepository.saveAndFlush(queriedOrder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OrderCountOutDTO count(String userId) {
        return count(userId, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OrderCountOutDTO count(String userId, Short orderType) {
        RegOrderDO orderDO = new RegOrderDO();
        orderDO.setUserId(Long.valueOf(userId));
        if (orderType != null) {
            orderDO.setOrderType(orderType);
        }
        return orderCount(orderDO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OrderCountOutDTO countByShopId(Long shopId) {
        RegOrderDO orderDO = new RegOrderDO();
        orderDO.setShopId(shopId);
        return orderCount(orderDO);
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    OrderCountOutDTO orderCount(RegOrderDO orderDO) {
        Assert.notNull(orderDO, "orderDo must not be null");
        if (!regOrderRepository.exists(Example.of(orderDO))) {
            return OrderCountOutDTO.of(0L);
        }
        orderDO.setOrderStatus(OrderStatusEnum.UNPAID.getStatusValue());
        long unPayNum = regOrderRepository.count(Example.of(orderDO));
        orderDO.setOrderStatus(OrderStatusEnum.UNCONSUME.getStatusValue());
        long unConsumeNum = regOrderRepository.count(Example.of(orderDO));
        orderDO.setOrderStatus(OrderStatusEnum.COMPLETED.getStatusValue());
        long completedNum = regOrderRepository.count(Example.of(orderDO));
        orderDO.setOrderStatus(OrderStatusEnum.CANCELED.getStatusValue());
        long cancelNum = regOrderRepository.count(Example.of(orderDO));
        return OrderCountOutDTO.of(unPayNum, unConsumeNum, completedNum, cancelNum);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePayStatus(OrderChangePayStatusInDTO inDTO) throws DBNullException {
        Optional<RegOrderDO> optional = Optional.ofNullable(regOrderRepository.findByOrderCode(inDTO.getOrderCode()));
        RegOrderDO order = optional.orElseThrow(DBNullException::new);
        short status = inDTO.getPayStatus();
        if (status != OrderStatusEnum.PayStatusEnum.PAID.getPayStatus()
                && status != OrderStatusEnum.PayStatusEnum.UNPAID.getPayStatus()) {
            throw new ServiceException("更改订单支付状态异常！支付状态入参不合法！");
        }
        order.setPayStatus(status);
        order.setOrderStatus(OrderStatusEnum.UNPAID.getStatusValue());
        regOrderRepository.saveAndFlush(order);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public Optional<RegOrderDO> findByOrderCode(String orderCode) {
        Assert.hasText(orderCode, "orderCode must not be null or empty");
        return Optional.ofNullable(regOrderRepository.findByOrderCode(orderCode));
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public List<RegOrderDO> findByItemId(Integer itemId) {
        Assert.notNull(itemId, "itemId must not be null");
        return regOrderRepository.findByItemId(itemId.longValue(), new Sort(Sort.Direction.DESC, "gmtCreate"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OrderDetailDTO getDetailByCode(String orderCode) throws DBNullException {
        Optional<RegOrderDO> optional = Optional.ofNullable(regOrderRepository.findByOrderCode(orderCode));
        RegOrderDO order = optional.orElseThrow(DBNullException::new);
        OrderDetailDTO out = new OrderDetailDTO();
        BeanUtils.copyProperties(order, out);
        out.setCreateTime(order.getGmtCreate().toLocalDateTime());
        out.setServiceStartTime(order.getServiceStartTime() == null ? null : order.getServiceStartTime().toLocalDateTime());
        out.setServiceEndTime(order.getServiceEndTime() == null ? null : order.getServiceEndTime().toLocalDateTime());
        out.setRefundTime(order.getRefundSuccessTime() == null ? null : order.getRefundSuccessTime().toLocalDateTime());
        try {
            Optional.ofNullable(shopService.getShopDetail(order.getShopId())).ifPresent(out::setShop);
        } catch (ServiceException e) {
            throw new RegimenProviderException("查询订单店铺详情异常", e);
        }
        try {
            Optional.ofNullable(itemService.getItemDetail(order.getItemId())).ifPresent(out::setItem);
        } catch (ServiceException e) {
            throw new RegimenProviderException("查询订单项目详情异常", e);
        }
        RegOrderDO regOrderDOFindNum = new RegOrderDO();
        regOrderDOFindNum.setShopId(order.getShopId());
        regOrderDOFindNum.setItemId(order.getItemId());
        regOrderDOFindNum.setUserId(order.getUserId());
        Long shopItemNum =  regOrderRepository.count(Example.of(regOrderDOFindNum)); 
        out.setShopItemNum(shopItemNum);
        return out;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OutDTO submitReview(final OrderReviewSubmitInDTO reviewData) throws DBNullException {
        Optional<RegOrderDO> optional = Optional.ofNullable(regOrderRepository.findByOrderCode(reviewData.getOrderCode()));
        RegOrderDO orderDO = optional.orElseThrow(DBNullException::new);
        if (reviewData.getSource().equals(OrderStatusEnum.ReviewSourceEnum.SHOP.getSource())) {
            if (orderDO.getShopReviewed().equals(OrderStatusEnum.IsReviewed.YES.getStatus())) {
                throw new ServiceException("订单已评价");
            }
            orderDO.setShopReviewed(OrderStatusEnum.IsReviewed.YES.getStatus());
        } else {
            if (orderDO.getReviewed().equals(OrderStatusEnum.IsReviewed.YES.getStatus())) {
                throw new ServiceException("订单已评价");
            }
            orderDO.setReviewed(OrderStatusEnum.IsReviewed.YES.getStatus());
        }
        reviewService.save(reviewData);
        regOrderRepository.save(orderDO);
        IncreaseReviewCountDTO countDTO = new IncreaseReviewCountDTO();
        countDTO.setItemId(orderDO.getItemId().intValue());
        countDTO.setCount(1);
        increaseReviewCountService.send(countDTO);
        if (reviewData.getSource().equals(OrderStatusEnum.ReviewSourceEnum.USER.getSource())) {
            // 用户评价订单奖励积分
            if (!rewardPointService.send(new RewardPointDTO(orderDO.getUserId().intValue(), 50, "订单评价"))) {
                throw new ServiceException("消息发送失败");
            }
        }
        return OutDTO.of(BOOLEAN_STRING_TRUE, "评价完成");
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OutDTO queryReview(String orderCode) {
        try {
            return OutDTO.of(BOOLEAN_STRING_TRUE, "OK", "OK", reviewService.findByOrderCode(orderCode));
        } catch (ServiceException e) {
            return OutDTO.of(Constant.BOOLEAN_STRING_FALSE, e.getMessage());
        }
    }

    private String generateUniqueCode() {
        int v = UUID.randomUUID().toString().hashCode();
        if (v < 0) {
            v = -v;
        }
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + String.format("%012d", v);
    }

    private Date calculationInvalidTime(Date date) {
        return new Date(date.getTime() + 60 * 60 * 1000);
    }

    @Scheduled(fixedDelay = 30 * 60 * 1000, initialDelay = 60 * 1000)
    public void scheduledEndService() {
        if (logger.isInfoEnabled()) {
            logger.info("执行一次24小时未完成订单扫描！");
        }
        LocalDateTime dateTime = LocalDateTime.now().minusHours(24L);
        Specification<RegOrderDO> querySpecification = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(cb.isNotNull(root.get("serviceStartTime")));
            predicates.add(cb.isNull(root.get("serviceEndTime")));
            predicates.add(cb.lessThan(root.get("serviceStartTime").as(Timestamp.class), Timestamp.valueOf(dateTime)));
            return query.where(predicates.toArray(new Predicate[0])).getRestriction();
        };
        List<RegOrderDO> list = regOrderRepository.findAll(querySpecification);
        list.forEach((order) -> {
            if (order.getServiceEndTime() == null) {
                order.setOrderStatus(OrderStatusEnum.COMPLETED.getStatusValue());
                order.setServiceEndTime(Timestamp.valueOf(LocalDateTime.now()));
                regOrderRepository.saveAndFlush(order);
            }
        });
    }

    @Transactional(rollbackFor = Exception.class)
    void updatePayStatus(@NotNull Map<String, String> paramMap, @NotNull RegOrderDO order) throws ParseException {
        if (!paramMap.containsKey("transaction_id") || !paramMap.containsKey("time_end")) {
            throw new IllegalArgumentException("transaction_id或time_end不存在");
        }
        order.setOrderStatus(OrderStatusEnum.UNCONSUME.getStatusValue());
        order.setPayStatus(OrderStatusEnum.PayStatusEnum.PAID.getPayStatus());
        order.setTransactionId(paramMap.get("transaction_id"));
        order.setPayEndTime(new SimpleDateFormat("yyyyMMddHHmmss").parse(paramMap.get("time_end")));
        regOrderRepository.saveAndFlush(order);
    }

}
